package com.loren.oauth.web.controller;

import com.loren.oauth.data.dto.ApiUserInfo;
import com.loren.oauth.data.dto.OnlineUserInfo;
import com.loren.oauth.data.dto.TokenInfo;
import com.loren.oauth.exception.BusinessException;
import com.loren.oauth.service.SecurityUserInfo;
import com.loren.oauth.utils.BeanConvertUtils;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@Controller
@AllArgsConstructor
@RequestMapping("/api")
public class ApiController {

    private final TokenStore tokenStore;

    private final StringRedisTemplate stringRedisTemplate;

    @ResponseBody
    @GetMapping("/user-info")
    public Object userInfo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication.getPrincipal();
    }

    @ResponseBody
    @GetMapping("/remove-token-session/{access_token}")
    public String removeAccessToken(@PathVariable("access_token") String accessToken) {
        String refreshToken = this.stringRedisTemplate.opsForValue().get("access_to_refresh:" + accessToken);

        OAuth2RefreshToken oAuth2RefreshToken = this.tokenStore.readRefreshToken(refreshToken);
        OAuth2AccessToken oAuth2AccessToken = this.tokenStore.readAccessToken(accessToken);

        if (oAuth2RefreshToken != null) {
            this.tokenStore.removeRefreshToken(oAuth2RefreshToken);
        }

        if (oAuth2AccessToken != null) {
            this.tokenStore.removeAccessToken(oAuth2AccessToken);
        }

        return "success";
    }

    @ResponseBody
    @GetMapping("/online-users")
    public Object online() {
        String prefix = "client_id_to_access:";
        Set<String> keys = this.stringRedisTemplate.keys(prefix + "*");
        Set<String> clientIds = new HashSet<>();
        for (String key : Objects.requireNonNull(keys)) {
            clientIds.add(StringUtils.substringAfter(key, prefix));
        }

        Map<String, List<OnlineUserInfo>> resultMap = new HashMap<>();
        for (String clientId : clientIds) {
            Collection<OAuth2AccessToken> tokenList = this.tokenStore.findTokensByClientId(clientId);
            List<OnlineUserInfo> onlineUserInfoList = new ArrayList<>();
            for (OAuth2AccessToken accessToken : tokenList) {
                OAuth2Authentication oAuth2Authentication = this.tokenStore.readAuthentication(accessToken);
                if (oAuth2Authentication != null) {
                    ApiUserInfo apiUserInfo = BeanConvertUtils.sourceToTarget(((SecurityUserInfo) oAuth2Authentication.getPrincipal()).getApiUserInfo(), ApiUserInfo.class);
                    TokenInfo tokenInfo = TokenInfo.builder().accessToken(accessToken.getValue()).tokenType(accessToken.getTokenType())
                            .refreshToken(accessToken.getRefreshToken().getValue()).scope(accessToken.getScope()).expiresIn(accessToken.getExpiresIn()).build();
                    onlineUserInfoList.add(OnlineUserInfo.builder().apiUserInfo(apiUserInfo).tokenInfo(tokenInfo).build());
                }
            }
            resultMap.put(clientId, onlineUserInfoList);
        }

        return resultMap;
    }

    @ResponseBody
    @GetMapping("/hello")
    @PreAuthorize("hasAuthority('super')")
    public String hello() {
        return "authorization server";
    }

    @ResponseBody
    @GetMapping("/test1")
    public String test1() {
//        return "success";
        throw new BusinessException("自定义异常");
    }

    @ResponseBody
    @GetMapping("/test2")
    public String test2(@RequestParam("p") String p) {
        return "success, p = " + p;
    }
}
